package org.convallaria.infrastruct.db.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 查询缓存管理器
 * 
 * 功能特性：
 * - 智能查询结果缓存
 * - 基于时间和访问频率的缓存淘汰
 * - 缓存命中率统计
 * - 缓存预热和刷新机制
 * - 内存使用监控和保护
 * 
 * @author gjh
 * @since 2024-09-24
 */
@Slf4j
public class QueryCacheManager {

    // 缓存配置
    private static final int DEFAULT_MAX_CACHE_SIZE = 1000;
    private static final long DEFAULT_EXPIRE_TIME_MINUTES = 30;
    private static final long DEFAULT_CLEANUP_INTERVAL_MINUTES = 10;
    private static final int DEFAULT_MEMORY_THRESHOLD = 80;
    
    // 缓存存储
    private final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
    
    // 配置参数
    private final int maxCacheSize;
    private final long expireTimeMinutes;
    private final long cleanupIntervalMinutes;
    private final int memoryThreshold;
    private final boolean enableCache;
    
    // 统计信息
    private final AtomicLong hitCount = new AtomicLong(0);
    private final AtomicLong missCount = new AtomicLong(0);
    private final AtomicLong evictionCount = new AtomicLong(0);
    
    // 清理任务线程池名称
    private final String cleanupExecutorName;
    
    /**
     * 缓存条目
     */
    private static class CacheEntry {
        private final Object value;
        private final LocalDateTime createTime;
        private volatile LocalDateTime lastAccessTime;
        private final AtomicLong accessCount;
        private final long size; // 估算的内存大小

        public CacheEntry(Object value, long estimatedSize) {
            this.value = value;
            this.createTime = LocalDateTime.now();
            this.lastAccessTime = LocalDateTime.now();
            this.accessCount = new AtomicLong(1);
            this.size = estimatedSize;
        }

        public Object getValue() {
            lastAccessTime = LocalDateTime.now();
            accessCount.incrementAndGet();
            return value;
        }

        public boolean isExpired(long expireMinutes) {
            return createTime.plusMinutes(expireMinutes).isBefore(LocalDateTime.now());
        }

        public boolean isStale(long staleMinutes) {
            return lastAccessTime.plusMinutes(staleMinutes).isBefore(LocalDateTime.now());
        }

        public long getAccessCount() { return accessCount.get(); }
        public LocalDateTime getCreateTime() { return createTime; }
        public LocalDateTime getLastAccessTime() { return lastAccessTime; }
        public long getSize() { return size; }
    }

    /**
     * 完整构造函数
     */
    public QueryCacheManager(boolean enableCache, int maxCacheSize, long expireTimeMinutes, 
                           long cleanupIntervalMinutes, int memoryThreshold) {
        this.enableCache = enableCache;
        this.maxCacheSize = maxCacheSize > 0 ? maxCacheSize : DEFAULT_MAX_CACHE_SIZE;
        this.expireTimeMinutes = expireTimeMinutes > 0 ? expireTimeMinutes : DEFAULT_EXPIRE_TIME_MINUTES;
        this.cleanupIntervalMinutes = cleanupIntervalMinutes > 0 ? cleanupIntervalMinutes : DEFAULT_CLEANUP_INTERVAL_MINUTES;
        this.memoryThreshold = memoryThreshold >= 50 && memoryThreshold <= 95 ? memoryThreshold : DEFAULT_MEMORY_THRESHOLD;
        
        // 生成唯一的线程池名称
        this.cleanupExecutorName = "QueryCacheCleanup-" + System.currentTimeMillis();
        
        if (enableCache) {
            // 使用企业级线程池管理器创建调度线程池
            ThreadPoolManager.createSingleThreadScheduledExecutor(cleanupExecutorName, true);
            startCleanupTask();
            log.info("查询缓存管理器已启动 - 最大缓存: {}, 过期时间: {}分钟, 清理间隔: {}分钟, 内存阈值: {}%, 线程池: {}", 
                this.maxCacheSize, this.expireTimeMinutes, this.cleanupIntervalMinutes, 
                this.memoryThreshold, cleanupExecutorName);
        } else {
            log.info("查询缓存已禁用");
        }
    }

    /**
     * 兼容性构造函数（保持向后兼容）
     */
    public QueryCacheManager(boolean enableCache, int maxCacheSize, long expireTimeMinutes) {
        this(enableCache, maxCacheSize, expireTimeMinutes, DEFAULT_CLEANUP_INTERVAL_MINUTES, DEFAULT_MEMORY_THRESHOLD);
    }

    /**
     * 默认构造函数
     */
    public QueryCacheManager() {
        this(false, DEFAULT_MAX_CACHE_SIZE, DEFAULT_EXPIRE_TIME_MINUTES);
    }

    /**
     * 获取缓存值
     */
    public Object get(String key) {
        if (!enableCache || !StringUtils.hasText(key)) {
            return null;
        }

        try {
            CacheEntry entry = cache.get(key);
            if (entry == null) {
                missCount.incrementAndGet();
                return null;
            }

            // 检查是否过期
            if (entry.isExpired(expireTimeMinutes)) {
                cache.remove(key);
                evictionCount.incrementAndGet();
                missCount.incrementAndGet();
                log.debug("缓存条目已过期并移除: {}", key);
                return null;
            }

            hitCount.incrementAndGet();
            log.debug("缓存命中: {}", key);
            return entry.getValue();
            
        } catch (Exception e) {
            log.warn("获取缓存时发生异常: {}", e.getMessage());
            missCount.incrementAndGet();
            return null;
        }
    }

    /**
     * 设置缓存值
     */
    public void put(String key, Object value) {
        if (!enableCache || !StringUtils.hasText(key) || value == null) {
            return;
        }

        try {
            // 估算对象大小
            long estimatedSize = estimateSize(value);
            
            // 检查内存限制
            if (shouldRejectCache(estimatedSize)) {
                log.debug("内存使用过高，拒绝缓存: {}", key);
                return;
            }

            // 检查缓存大小限制
            if (cache.size() >= maxCacheSize) {
                evictLeastUsed();
            }

            CacheEntry entry = new CacheEntry(value, estimatedSize);
            cache.put(key, entry);
            
            log.debug("缓存已设置: {} (大小: {}字节)", key, estimatedSize);
            
        } catch (Exception e) {
            log.warn("设置缓存时发生异常: {}", e.getMessage());
        }
    }

    /**
     * 移除缓存
     */
    public void remove(String key) {
        if (!enableCache || !StringUtils.hasText(key)) {
            return;
        }

        try {
            CacheEntry removed = cache.remove(key);
            if (removed != null) {
                evictionCount.incrementAndGet();
                log.debug("缓存已移除: {}", key);
            }
        } catch (Exception e) {
            log.warn("移除缓存时发生异常: {}", e.getMessage());
        }
    }

    /**
     * 清空所有缓存
     */
    public void clear() {
        if (!enableCache) {
            return;
        }

        try {
            int size = cache.size();
            cache.clear();
            evictionCount.addAndGet(size);
            log.info("所有缓存已清空，共移除 {} 个条目", size);
        } catch (Exception e) {
            log.warn("清空缓存时发生异常: {}", e.getMessage());
        }
    }

    /**
     * 检查缓存是否存在
     */
    public boolean contains(String key) {
        if (!enableCache || !StringUtils.hasText(key)) {
            return false;
        }

        try {
            CacheEntry entry = cache.get(key);
            if (entry == null) {
                return false;
            }

            // 检查是否过期
            if (entry.isExpired(expireTimeMinutes)) {
                cache.remove(key);
                evictionCount.incrementAndGet();
                return false;
            }

            return true;
        } catch (Exception e) {
            log.warn("检查缓存存在性时发生异常: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 淘汰最少使用的缓存条目
     */
    private void evictLeastUsed() {
        try {
            if (cache.isEmpty()) {
                return;
            }

            // 找到访问次数最少且最久未访问的条目
            String evictKey = cache.entrySet().stream()
                .min((e1, e2) -> {
                    CacheEntry entry1 = e1.getValue();
                    CacheEntry entry2 = e2.getValue();
                    
                    // 首先比较访问次数
                    long accessDiff = entry1.getAccessCount() - entry2.getAccessCount();
                    if (accessDiff != 0) {
                        return Long.compare(entry1.getAccessCount(), entry2.getAccessCount());
                    }
                    
                    // 访问次数相同时，比较最后访问时间
                    return entry1.getLastAccessTime().compareTo(entry2.getLastAccessTime());
                })
                .map(Map.Entry::getKey)
                .orElse(null);

            if (evictKey != null) {
                cache.remove(evictKey);
                evictionCount.incrementAndGet();
                log.debug("淘汰最少使用的缓存条目: {}", evictKey);
            }
        } catch (Exception e) {
            log.warn("淘汰缓存时发生异常: {}", e.getMessage());
        }
    }

    /**
     * 启动清理任务
     */
    private void startCleanupTask() {
        try {
            ScheduledExecutorService executor = ThreadPoolManager.getScheduledThreadPool(cleanupExecutorName);
            if (executor != null) {
                executor.scheduleAtFixedRate(this::cleanup, 
                    cleanupIntervalMinutes, 
                    cleanupIntervalMinutes, 
                    TimeUnit.MINUTES);
                log.debug("缓存清理任务已启动，间隔: {}分钟", cleanupIntervalMinutes);
            } else {
                log.error("无法获取清理任务线程池: {}", cleanupExecutorName);
            }
        } catch (Exception e) {
            log.error("启动缓存清理任务失败", e);
        }
    }

    /**
     * 清理过期和陈旧的缓存条目
     */
    private void cleanup() {
        try {
            int removedCount = 0;
            
            for (Map.Entry<String, CacheEntry> entry : cache.entrySet()) {
                CacheEntry cacheEntry = entry.getValue();
                
                // 移除过期条目
                if (cacheEntry.isExpired(expireTimeMinutes)) {
                    cache.remove(entry.getKey());
                    removedCount++;
                    continue;
                }
                
                // 移除长时间未访问的条目（2倍过期时间）
                if (cacheEntry.isStale(expireTimeMinutes * 2)) {
                    cache.remove(entry.getKey());
                    removedCount++;
                }
            }
            
            if (removedCount > 0) {
                evictionCount.addAndGet(removedCount);
                log.debug("缓存清理完成，移除 {} 个条目", removedCount);
            }
            
        } catch (Exception e) {
            log.warn("缓存清理时发生异常", e);
        }
    }

    /**
     * 估算对象大小
     */
    private long estimateSize(Object value) {
        if (value == null) {
            return 0;
        }

        try {
            // 简单的大小估算逻辑
            if (value instanceof String) {
                return ((String) value).length() * 2L; // UTF-16编码
            } else if (value instanceof java.util.Collection) {
                return ((java.util.Collection<?>) value).size() * 100L; // 假设每个元素100字节
            } else if (value instanceof java.util.Map) {
                return ((java.util.Map<?, ?>) value).size() * 150L; // 假设每个键值对150字节
            } else {
                return 200L; // 默认大小
            }
        } catch (Exception e) {
            log.debug("估算对象大小时发生异常: {}", e.getMessage());
            return 200L; // 返回默认大小
        }
    }

    /**
     * 检查是否应该拒绝缓存（基于内存使用）
     */
    private boolean shouldRejectCache(long objectSize) {
        try {
            Runtime runtime = Runtime.getRuntime();
            long usedMemory = runtime.totalMemory() - runtime.freeMemory();
            long maxMemory = runtime.maxMemory();
            
            double memoryUsageRatio = (double) usedMemory / maxMemory * 100;
            
            // 如果内存使用超过配置阈值，拒绝大对象缓存
            if (memoryUsageRatio > memoryThreshold && objectSize > 10000) {
                log.debug("内存使用率 {:.1f}% 超过阈值 {}%，拒绝大对象缓存", memoryUsageRatio, memoryThreshold);
                return true;
            }
            
            // 如果内存使用超过配置阈值+10%，拒绝所有缓存
            double rejectAllThreshold = Math.min(memoryThreshold + 10, 95);
            if (memoryUsageRatio > rejectAllThreshold) {
                log.debug("内存使用率 {:.1f}% 超过严格阈值 {:.1f}%，拒绝所有缓存", memoryUsageRatio, rejectAllThreshold);
                return true;
            }
            
            return false;
            
        } catch (Exception e) {
            log.debug("检查内存使用时发生异常: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取缓存统计信息
     */
    public String getCacheStatistics() {
        if (!enableCache) {
            return "缓存已禁用";
        }

        StringBuilder stats = new StringBuilder();
        stats.append("\n=== 查询缓存统计 ===\n");
        
        long total = hitCount.get() + missCount.get();
        double hitRatio = total > 0 ? (double) hitCount.get() / total * 100 : 0;
        
        stats.append("缓存条目数: ").append(cache.size()).append("/").append(maxCacheSize).append("\n");
        stats.append("缓存命中数: ").append(hitCount.get()).append("\n");
        stats.append("缓存未命中数: ").append(missCount.get()).append("\n");
        stats.append("缓存命中率: ").append(String.format("%.2f%%", hitRatio)).append("\n");
        stats.append("淘汰次数: ").append(evictionCount.get()).append("\n");
        stats.append("过期时间: ").append(expireTimeMinutes).append("分钟\n");
        stats.append("清理间隔: ").append(cleanupIntervalMinutes).append("分钟\n");
        stats.append("内存阈值: ").append(memoryThreshold).append("%\n");
        stats.append("当前内存使用: ").append(String.format("%.2f%%", getCurrentMemoryUsage())).append("\n");
        stats.append("线程池名称: ").append(cleanupExecutorName).append("\n");
        
        // 内存使用情况
        long totalSize = cache.values().stream()
            .mapToLong(CacheEntry::getSize)
            .sum();
        stats.append("估算内存使用: ").append(totalSize / 1024).append("KB\n");
        
        // 热点缓存条目
        stats.append("\n=== 热点缓存（Top 5） ===\n");
        cache.entrySet().stream()
            .sorted((e1, e2) -> Long.compare(e2.getValue().getAccessCount(), e1.getValue().getAccessCount()))
            .limit(5)
            .forEach(entry -> {
                CacheEntry cacheEntry = entry.getValue();
                stats.append(String.format("%-50s | 访问次数: %6d | 创建时间: %s\n",
                    entry.getKey().length() > 50 ? entry.getKey().substring(0, 47) + "..." : entry.getKey(),
                    cacheEntry.getAccessCount(),
                    cacheEntry.getCreateTime().toString().substring(11, 19)));
            });
        
        return stats.toString();
    }

    /**
     * 预热缓存
     */
    public void warmup(Map<String, Object> warmupData) {
        if (!enableCache || warmupData == null || warmupData.isEmpty()) {
            return;
        }

        try {
            log.info("开始缓存预热，数据量: {}", warmupData.size());
            
            for (Map.Entry<String, Object> entry : warmupData.entrySet()) {
                put(entry.getKey(), entry.getValue());
            }
            
            log.info("缓存预热完成，当前缓存条目数: {}", cache.size());
            
        } catch (Exception e) {
            log.error("缓存预热时发生异常", e);
        }
    }

    /**
     * 关闭缓存管理器
     */
    public void shutdown() {
        try {
            // 使用ThreadPoolManager统一关闭线程池
            if (enableCache && StringUtils.hasText(cleanupExecutorName)) {
                ThreadPoolManager.shutdownPool(cleanupExecutorName);
                log.debug("清理任务线程池 [{}] 已关闭", cleanupExecutorName);
            }
            
            // 清空所有缓存
            clear();
            log.info("查询缓存管理器已关闭 - 线程池: {}", cleanupExecutorName);
            
        } catch (Exception e) {
            log.warn("关闭缓存管理器时发生异常", e);
        }
    }

    // ===== Getter方法 =====
    
    public boolean isEnabled() { return enableCache; }
    public int getMaxCacheSize() { return maxCacheSize; }
    public long getExpireTimeMinutes() { return expireTimeMinutes; }
    public long getCleanupIntervalMinutes() { return cleanupIntervalMinutes; }
    public int getMemoryThreshold() { return memoryThreshold; }
    public int getCurrentSize() { return cache.size(); }
    public long getHitCount() { return hitCount.get(); }
    public long getMissCount() { return missCount.get(); }
    public long getEvictionCount() { return evictionCount.get(); }
    public String getCleanupExecutorName() { return cleanupExecutorName; }
    
    public double getHitRatio() {
        long total = hitCount.get() + missCount.get();
        return total > 0 ? (double) hitCount.get() / total * 100 : 0;
    }

    /**
     * 获取当前内存使用率
     */
    public double getCurrentMemoryUsage() {
        try {
            Runtime runtime = Runtime.getRuntime();
            long usedMemory = runtime.totalMemory() - runtime.freeMemory();
            long maxMemory = runtime.maxMemory();
            return (double) usedMemory / maxMemory * 100;
        } catch (Exception e) {
            return 0.0;
        }
    }
}
